//const regx = /[a-z]+\..+/; //获取文件名
const fileio = require("@ohos.fileio");
const path = require("path");
const { ReadStream, WriteStream } = require("./streams");
const errorcode = require('./error-code');
const { fromPromise, fromCallback } = require('./universalify/index');
const tag = "NODEPOLYFILL:";
const { Buffer } = require('../buffer/index');
function toArrayBuffer(buf) {
    var ab = new ArrayBuffer(buf.length);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        view[i] = buf[i];
    }
    return ab;
}

function toBuffer(buf, ab) {
    var view = new Uint8Array(ab);
    for (var i = 0; i < buf.length; ++i) {
        buf[i] = view[i];
    }
    return buf;
}

function modifyErrorCode(err, code) {
    if (err && code) {
        err.code = code;
        err.message = errorcode.errormessage[code];
    } else if (err && err.message) {
        err.code = errorcode.message2code[err.message];
    }
}

function access(path, mode, callback) {
    fileio.access(path, mode, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function accessSync(path, mode) {
    try {

        fileio.accessSync(path, mode);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function appendFile(path, data, options, callback) { }

function appendFileSync(path, data, options) { }

function chmod(path, mode, callback) {
    fileio.chmod(path, mode, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function chmodSync(path, mode) {
    try {
        fileio.chmodSync(path, mode);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function chown(path, uid, gid, callback) {

    fileio.chown(path, uid, gid, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function chownSync(path, uid, gid) {
    try {
        fileio.chownSync(path, uid, gid);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}



function close(fd, callback) {

    fileio.close(fd, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function closeSync(fd) {
    try {
        fileio.closeSync(fd);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function copyFile(src, dest, mode, callback) {

    fileio.copyFile(src, dest, mode, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function copyFileSync(src, dest, mode) {
    try {
        fileio.copyFileSync(src, dest, mode);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function cp(src, dest, options, callback) { }

function existsSync(path) {
    try {
        fileio.accessSync(path);
        return true;
    } catch (err) {
        return false;
    }
}


function fchmod(fd, mode, callback) {

    fileio.fchmod(fd, mode, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function fchmodSync(fd, mode) {
    try {
        fileio.fchmodSync(fd, mode);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function fchown(fd, uid, gid, callback) {

    fileio.fchown(fd, uid, gid, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function fchownSync(fd, uid, gid) {
    try {
        fileio.fchownSync(fd, uid, gid);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function fdatasync(fd, callback) {

    fileio.fdatasync(fd, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function fdatasyncSync(fd) {
    try {
        fileio.fdatasyncSync(fd);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function fstat(fd, options, callback) {
    fileio.fstat(fd, (err, stat) => {
        modifyErrorCode(err);
        callback(err, stat);
    });
}

function fstatSync(fd, options) {
    try {
        return fileio.fstatSync(fd);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function fsync(fd, callback) {
    fileio.fsync(fd, (err) => {
        modifyErrorCode(err);
        callback(err);
    })
}

function fsyncSync(fd) {
    try {
        return fileio.fsyncSync(fd);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function ftruncate(fd, len, callback) {
    fileio.ftruncate(fd, len, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function ftruncateSync(fd, len) {
    try {
        return fileio.ftruncateSync(fd, len);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function futimes(fd, atime, mtime, callback) { }

function futimesSync(fd, atime, mtime) { }

function lchmod(path, mode, callback) { }

function lchmodSync(path, mode) { }


function lchown(path, uid, gid, callback) {
    fileio.lchown(path, uid, gid, (err) => {
        modifyErrorCode(err);
        callback(err);
    })
}

function lchownSync(path, uid, gid) {
    try {
        return fileio.lchownSync(path, uid, gid);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function lutimes(path, atime, mtime, callback) { }

function lutimesSync(path, atime, mtime) { }

function link(existingPath, newPath, callback) { }

function linkSync(existingPath, newPath) { }


function lstat(path, options, callback) {
    fileio.lstat(path, (err, stat) => {
        modifyErrorCode(err);
        callback(err, stat);
    });
}

function lstatSync(path, options) {
    try {
        return fileio.lstatSync(path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function mkdir(dir, options, callback) {
    if (options && options.recursive) {
        let parentPath = path.dirname(dir);
        access(parentPath, undefined, (err) => {
            if (err) {
                try {
                    mkdirSync(parentPath, options);
                } catch (err) {
                    modifyErrorCode(err);
                    callback(err);
                }
            }
            if (options && options.mode) {
                fileio.mkdir(dir, options.mode, (err) => {
                    modifyErrorCode(err);
                    callback(err);
                })
            } else {
                fileio.mkdir(dir, (err) => {
                    modifyErrorCode(err);
                    callback(err);
                })
            }
        })
    } else {
        if (options && options.mode) {
            fileio.mkdir(dir, options.mode, (err) => {
                modifyErrorCode(err);
                callback(err);
            })
        } else {
            fileio.mkdir(dir, (err) => {
                modifyErrorCode(err);
                callback(err);
            })
        }
    }
}

function mkdirSync(dir, options) {
    let parentPath = path.dirname(dir);
    if (
        options &&
        options.recursive &&
        parentPath !== path.sep &&
        !existsSync(parentPath)
    ) {
        mkdirSync(parentPath, options);
    }
    try {
        if (options && options.mode) {
            return fileio.mkdirSync(dir, options.mode);
        } else {
            return fileio.mkdirSync(dir);
        }

    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function mkdtemp(preifx, options, callback) {
    fileio.mkdtemp(preifx, (err, directory) => {
        modifyErrorCode(err);
        callback(err, directory);
    });
}

function mkdtempSync(preifx, options) {
    try {
        return fileio.mkdtempSync(preifx);
    } catch (err) {
        modifyErrorCode(err);
        throw err;

    }
}

function open(path, flags, mode, callback) {
    fileio.open(path, flags, mode, (err, fd) => {
        modifyErrorCode(err);
        callback(err, fd);
    });
}

function openSync(path, flags, mode) {
    try {
        return fileio.openSync(path, flags, mode);
    } catch (err) {
        modifyErrorCode(err);
        throw err;

    }
}

function opendir(path, options, callback) {
    fileio.opendir(path, (err, dir) => {
        modifyErrorCode(err);
        callback(err, dir);
    });
}

function opendirSync(path, options) {
    try {
        return fileio.opendirSync(path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;

    }
}

function read(fd, buffer, offset, length, position, callback) {
    let ab;
    let options;

    if (typeof buffer === 'function') {
        callback = buffer;
        let b = Buffer.alloc(16384);
        ab = b.buffer;

        options = { offset: 0, length: b.byteLength - offset, position: null };

        fileio.read(fd, ab, (err, readOut) => {
            modifyErrorCode(err);
            callback(err, readOut.bytesRead, b);
        });
        return;
    } else if (typeof offset === 'function') {
        callback = offset;
        options = buffer;
    } else {
        options = { buffer, offset, length, position };
    }

    if (options && options.buffer && options.buffer.buffer) {
        ab = options.buffer.buffer;

        fileio.read(
            fd,
            ab,
            {
                offset: options.offset,
                length: options.length,
                position: options.position,
            },
            (err, readOut) => {
                modifyErrorCode(err);
                callback(
                    err,
                    readOut.bytesRead,
                    toBuffer(options.buffer, readOut.buffer)
                );
            }
        )
    } else {
        fileio.read(fd, new ArrayBuffer(length), (err, readOut) => {
            modifyErrorCode(err);
            callback(
                err,
                readOut.bytesRead,
                toBuffer(Buffer.alloc(16384), readOut.buffer)
            );
        })
    }
}

function readSync(fd, buffer, offset, length, position) {
    let ab;
    let options;
    if (typeof offset === 'object') {
        options = offset;

    } else {
        options = {
            offset, 
            length, 
            position,
        };
    }
    if (buffer && buffer.buffer) {
        ab = buffer.buffer;
    }
    try {
        return fileio.readSync(fd, ab);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }

}

function readdir(path, options, callback) {
    if (typeof options === 'function') {
        callback = options;
    }
    fileio.access(path, (err) => {
        if (err) {
            modifyErrorCode(err);
            callback(err);
        }
        fileio.stat(path, (err, stat) => {
            if (err) {
                modifyErrorCode(err);
                callback(err);
            }
            if (stat.isDirectory()) {
                fileio.opendir(path, (err, dir) => {
                    if (err) {
                        modifyErrorCode(err);
                        callback(err);
                    }
                    let list = [];
                    while (true) {
                        try {
                            let dirent = dir.readSync();
                            list.push(dirent.name);
                        } catch (err) {
                            break;
                        }
                    }
                    callback(undefined, list);
                });
            } else {
                let err = new Error();
                modifyErrorCode(err, "ENOTDIR");
                callback(err);
            }
        });
    });
}

function readdirSync(path, options) {
    try {
        fileio.accessSync(path);
        let stat = fileio.statSync(path);
        if (stat.isDirectory()) {
            let dir = fileio.opendirSync(path);
            let list = [];
            while (true) {
                try {
                    let dirent = dir.readSync();
                    list.push(dirent.name);
                } catch (err) {
                    break;
                }
            }
            return list;
        }

    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}


function readFile(path, options, callback) {
    fileio.readText(path, {}, (err, data) => {
        modifyErrorCode(err);
        callback(err,data);
    });
}

function readFileSync(path, options) {
    try {
        let reader = path;
        if (typeof path == 'string') {
            reader = fileio.openSync(path, 0o102, 0o666);
        }

        let arrayBuffer = new ArrayBuffer(20000);

        fileio.readSync(reader, arrayBuffer);
        return new Uint8Array(arrayBuffer);

    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function readlink(path, options, callback) { }

function readlinkSync(path, options) { }

function readv(fd, buffers, position, callback) {
    Promise.resolve().then(() => {
        try {
            let bytesRead = readvSync(fd, buffers, position);
            callback(undefined, bytesRead, buffers);
        } catch (err) {
            modifyErrorCode(err);
            callback(err);
        }
    })
}

function readvSync(fd, buffers, position) {
    let options;

    if (position && typeof position === 'number') {
        options = { position };
    } else {
        position = 0;
        options = { position };
    }

    let bytesRead = 0;
    buffers.forEach((v, k) => {
        try {
            bytesRead += fileio.readSync(fd, buffers[k].buffer, {
                 position: options,
                });
            options.position = position + bytesRead;
        } catch(err) {
            modifyErrorCode(err);
            throw err;
        }
    });

    return bytesRead;
}

function realpath(path, options, callback) {
    callback(undefined, path);
}
function realpathSync(path, options) {
    return path;
}

function rename(oldPath, newPath, callback) {
    fileio.rename(oldPath, newPath, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function renameSync(oldPath, newPath) {
    try {
        fileio.renameSync(oldPath, newPath);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function rmdir(path, callback) {
    fileio.rmdir(path, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function rmdirSync(path, options) {
    try {
        fileio.rmdirSync(path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function rm(path, options, callback) {
    Promise.resolve().then(() => {
        try {
            rmSync(path, options);
            callback(undefined);
        } catch (err) {
            modifyErrorCode(err);
            callback(err);
        }
    });
}

function rmSync(path, options) {
    let st;
    try {
        st = fileio.lstatSync(path);
    } catch (er) {
        if (er.message === errorcode.errormessage.ENOENT) {
            return;
        }
    }

    try {
        if (st && st.isDirectory()) {
            _rmdirSync(path, null);
        } else {
            fileio.unlinkSync(path);
        }
    } catch (er) {
        if (er.message === errorcode.errormessage.ENOENT) {
            return;
        } else if (er.message === errorcode.errormessage.EPERM) {
            _rmdirSync(path, er);
        } else if (er.message !== errorcode.errormessage.EISDIR) {
            throw er;
        }
        _rmdirSync(path, er);
    }
}

function _rmdirSync(p, originalEr) {
    try {
        fileio.rmdirSync(p);
    } catch (er) {
        if (er.message === errorcode.errormessage.ENOTDIR) {
            throw originalEr;
        } else if (
            er.message === errorcode.errormessage.ENOTEMPTY ||
            er.message === errorcode.errormessage.EEXIST ||
            er.message === errorcode.errormessage.EPERM
            ) {
            _rmkidsSync(p);
        } else if (er.message !== errorcode.errormessage.ENOENT) {
            throw er;
        }
    }
}

function _rmkidsSync(p) {
    readdirSync(p, undefined).forEach((f) => rmSync(path.join(p, f), undefined));
    const ret = fileio.rmdirSync(p);
    return ret;
}

function stat(path, options, callback) {
    fileio.stat(path, (err, stat) => {
        modifyErrorCode(err);
        callback(err, stat);
    });
}

function statSync(path, options) {
    try {
        fileio.statSync(path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function symlink(target, path, callback) {
    fileio.symlink(target, path, (err) => {
        modifyErrorCode(err);
        callback(err);
    });
}

function symlinkSync(target, path, type) {
    try {
        fileio.symlinkSync(target, path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function truncate(path, len, callback) {
    fileio.truncate(path, len, (err) => {
        modifyErrorCode(err);
        callback(err);
    })
}

function truncateSync(path, len) {
    try {
        fileio.truncateSync(path, len);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function unlink(path, callback) {
    fileio.unlink(path, (err) => {
        modifyErrorCode(err);
        callback(err);
    })
}

function unlinkSync(path) {
    try {
        fileio.unlinkSync(path);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function utimes(path, atime, mtime) { }
function utimesSync(path, atime, mtime) { }

function writeFile(file, data, options, callback) {
    if (typeof file === 'number') {
        fileio.write(file, data, options, (err) => {
            modifyErrorCode(err);
            callback(err);
        });
    } else {
        fileio
            .open(file, 0o2 | 0o100, 0o666)
            .then((fd) => {
                fileio.write(fd, data, options, (err) => {
                    modifyErrorCode(err);
                    callback(err);
                })
            })
            .catch((err) => {
                modifyErrorCode(err);
                callback(err);
            })
    }
}


function writeFileSync(file, data, options) {
    try {
        if (data instanceof Uint8Array || data instanceof DataView) {
            data = data.buffer;
        }
        if (typeof file === 'number') {
            return fileio.writeSync(file, data);
        }
        let fd = fileio.openSync(file, 0o2 | 0o100, 0o666);
        return fileio.writeSync(fd, data);
    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }

}


function write(fd, buffer, offset, length, position, callback) {
    if (typeof buffer === 'string') {
        position = offset;
        let encoding = length;
        fileio.write(fd, buffer, { position, encoding }, (err, len) => {
            modifyErrorCode(err);
            callback(err, len);
        });
    } else {
        return fileio.write(
            fd,
            buffer,
            { offset, length, position },
            (err, len) => {
                modifyErrorCode(err);
                callback(err, len);
            }
        );
    }
}

function writeSync(fd, buffer, offset, length, position) {
    try {

        let options;
        if (typeof buffer === 'string') {
            if (offset) {
                position = offset;
                options = { position };
            }
        } else {
            buffer = buffer.buffer;
            if (typeof offset === 'object') {
                options = offset;
            } else if (typeof offset === 'number') {
                options = {
                    offset,
                }
                if (length && typeof length === 'number') {
                    options[length] = length;
                }
                if (position && typeof position === 'number') {
                    options[position] = position;
                }
            }
        }
        return fileio.writeSync(fd, buffer, options);

    } catch (err) {
        modifyErrorCode(err);
        throw err;
    }
}

function writev(fd, buffers, position, callback) {
    Promise.resolve().then(() => {
        try {
            let bytesWritten = writevSync(fd, buffers, position);
            callback(undefined, bytesWritten, buffers);
        } catch (err) {
            modifyErrorCode(err);
            callback(err);
        }
    });
}

function writevSync(fd, buffers, position) {
    let options;
    if (position && typeof position === 'number') {
        options = { position };
    } else {
        position = 0;
        options = { position };
    }

    let bytesWritten = 0;
    buffers.forEach((v, k) => {
        try {

            bytesWritten += fileio.writeSync(fd, buffers[k].buffer, { 
                position: options,
             });
            options.position = position + bytesWritten;
        } catch (err) {
            modifyErrorCode(err);
            throw err;
        }
    });
    return bytesWritten;
}


function createReadStream(path, options) {
    return new ReadStream(path, options);
}

function createWriteStream(path, options) {
    return new WriteStream(path, options);
}

module.exports = {
    access: fromCallback(access),
    accessSync,
    chmod: fromCallback(chmod),
    chmodSync,
    chown: fromCallback(chown),
    chownSync,
    close: fromCallback(close),
    closeSync,
    copyFile: fromCallback(copyFile),
    copyFileSync,
    existsSync,
    fchmod: fromCallback(fchmod),
    fchmodSync,
    fchown: fromCallback(fchown),
    fchownSync,
    fdatasync: fromCallback(fdatasync),
    fdatasyncSync,
    fstat: fromCallback(fstat),
    fstatSync,
    fsync: fromCallback(fsync),
    fsyncSync,
    ftruncate: fromCallback(ftruncate),
    ftruncateSync,
    lchown: fromCallback(lchown),
    lchownSync,
    lstat: fromCallback(lstat),
    lstatSync,
    mkdir: fromCallback(mkdir),
    mkdirSync,
    mkdtemp: fromCallback(mkdtemp),
    mkdtempSync,
    open: fromCallback(open),
    openSync,
    opendir: fromCallback(opendir),
    opendirSync,
    read: fromCallback(read),
    readSync,

    readdir: function () { },
    readdirSync: function () { },

    readFile: fromCallback(readFile),
    readFileSync,
    readv: fromCallback(readv),
    readvSync,
    realpath: fromCallback(realpath),
    realpathSync,
    rename: fromCallback(rename),
    renameSync,
    rmdir: fromCallback(rmdir),
    rmdirSync,
    rm: fromCallback(rm),
    rmSync,
    stat: fromCallback(stat),
    statSync,
    symlink: fromCallback(symlink),
    symlinkSync,
    truncate: fromCallback(truncate),
    truncateSync,
    unlink: fromCallback(unlink),
    unlinkSync,

    writeFile: fromCallback(writeFile),
    writeFileSync,
    write: fromCallback(write),
    writeSync,
    writev: fromCallback(writev),
    writevSync,
    createReadStream,
    createWriteStream,
};